﻿namespace Whut.ObjectSharePointMapper.Internals
{
    using System;
    using System.Collections.Generic;
    using System.Reflection;

    public class MetaDataStore
    {
        private readonly Dictionary<Type, ListInfo> typeToListInfo = new Dictionary<Type, ListInfo>();

        public void BuildMetadataFor(Assembly assembly)
        {
            var newTypeToListInfo = this.BuildTypeToListInfoMapping(assembly);
            foreach (KeyValuePair<Type, ListInfo> pair in newTypeToListInfo)
            {
                foreach (PropertyInfo propertyInfo in pair.Key.GetProperties())
                {
                    SetIdFieldInfoInListInfo(pair.Value, propertyInfo);
                    AddFieldInfoToListInfo(pair.Value, propertyInfo);
                }

                this.typeToListInfo.Add(pair.Key, pair.Value);
            }
        }

        public ListInfo GetListInfoFor<TEntity>()
        {
            return this.GetListInfoFor(typeof(TEntity));
        }

        public ListInfo GetListInfoFor(Type entityType)
        {
            if (!this.typeToListInfo.ContainsKey(entityType))
            {
                throw new ArgumentOutOfRangeException("entityType", "There is no ListInfo for given entity type.");
            }

            return this.typeToListInfo[entityType];
        }

        private static void SetIdFieldInfoInListInfo(ListInfo listInfo, PropertyInfo propertyInfo)
        {
            IdAttribute idAttribute = propertyInfo.GetAttribute<IdAttribute>();
            if (idAttribute != null)
            {
                if (listInfo.Id != null)
                {
                    throw new InvalidOperationException("IdAttribute used more than once on single entity class.");
                }

                listInfo.Id = new FieldInfo(IdAttribute.FieldId, propertyInfo);
            }
        }

        private static void AddFieldInfoToListInfo(ListInfo listInfo, PropertyInfo propertyInfo)
        {
            FieldAttribute fieldAttribute = propertyInfo.GetAttribute<FieldAttribute>();
            if (fieldAttribute != null)
            {
                listInfo.AddFieldInfo(fieldAttribute.FieldId, propertyInfo);
            }
        }

        private Dictionary<Type, ListInfo> BuildTypeToListInfoMapping(Assembly assembly)
        {
            Dictionary<Type, ListInfo> newTypeToListInfo = new Dictionary<Type, ListInfo>();
            foreach (Type type in assembly.GetTypes())
            {
                ListAttribute listAttribute = type.GetAttribute<ListAttribute>();

                if (listAttribute != null)
                {
                    ListInfo listInfo = new ListInfo(listAttribute.ListRelativeUrl);
                    newTypeToListInfo.Add(type, listInfo);
                }
            }

            return newTypeToListInfo;
        }
    }
}
